package com.pb.service.boot.config;

import com.pb.infra.auth.entity.custom.PriviInterCustom;
import com.pb.infra.oth.util.JwtTokenConfig;
import com.pb.infra.auth.entity.custom.PriviRoleCustom;
import com.pb.infra.auth.mapper.CommonMapper;
import com.pb.infra.auth.service.PriviUserService;
import com.pb.infra.oth.util.Constant;
import com.pb.infra.oth.util.StringUtil;
import com.pb.infra.oth.util.UserInfo;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.concurrent.TimeUnit;


/**
 * 权限资源
 *这个类的作用，主要是根据用户传来的请求地址，分析出请求需要的角色
 * @Author:liudebao
 * @Date: 2021/1/3
 */
@Slf4j
@Component
public class CustomFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private PriviUserService userService;

    @Autowired
    private UserDetailService userDetailService;

    @Autowired
    private CommonMapper commonMapper;

    @Resource
    private JwtTokenConfig jwtTokenConfig;

    AntPathMatcher antPathMatcher = new AntPathMatcher();

    /*
    根据用户角色过滤接口权限
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        String requestUrl = ((FilterInvocation) object).getRequestUrl();
        HttpServletRequest request = ((FilterInvocation) object).getRequest();
        HttpServletResponse response =  ((FilterInvocation) object).getResponse();
        String method = request.getMethod(); //请求方式
        if ("OPTIONS".equalsIgnoreCase(method)) {
            return SecurityConfig.createList("PASS");
        }
        String userId = "";
        boolean isRedisExist = true;
        boolean isTokenValid = true;
        String token = "";
        if (!requestUrl.contains("/login/loginCheck") && requestUrl.indexOf("/druid/") < 0 && requestUrl.indexOf("/favicon.ico") < 0) {
            token = request.getHeader(jwtTokenConfig.getHeader());
            isTokenValid = validToken(token, response, request);
            if (isTokenValid) {
                Claims claims = jwtTokenConfig.getClaimByToken(token);
                userId = (String)claims.get(jwtTokenConfig.getSub());
                if (!redisTemplate.hasKey(token)) {
                    isRedisExist = false;
                    log.warn("redis没找到信息 =========》interface=" + requestUrl + "@@key=" + token);
                }
            } else {
                log.warn("token无效 =========》userId=" + userId + "@@token=" + token);
            }
        } else {
            return SecurityConfig.createList("PASS");
        }
        if (isRedisExist && isTokenValid) {
            //超级管理员
            if (Constant.SYSTEM_ADMIN.equals(userId)) {
                return SecurityConfig.createList("PASS");
            }
            //切换职位标志
            List<PriviInterCustom> menus = new ArrayList<>();
            List<PriviInterCustom> allInters = new ArrayList<>();
            //切换职位获取数据更新session
            UserInfo userInfo = (UserInfo)redisTemplate.opsForValue().get(token);
            //从redis里面获取菜单信息
            if (userInfo.getPositionMenus() == null || userInfo.getPositionMenus().size() == 0 || "1".equals(userInfo.getMenuFlag())) {
                Map<String, Object> map1 = new HashMap<>();
                map1.put("userId", userId);
                map1.put("positionId", userInfo.getPositionId());
                menus = userService.selAdminRoleMenuByUserId(map1);
                userInfo.setPositionMenus(menus);
                userInfo.setMenuFlag("0");
                if (redisTemplate.hasKey(token)) {
                    redisTemplate.delete(token);
                }
                redisTemplate.opsForValue().set(token, userInfo, 24L, TimeUnit.HOURS);
            } else {
                menus = userInfo.getPositionMenus();
            }
            if (!"1".equals(userInfo.getAllIntersFlag())) {
                Map<String, Object> userMap = new HashMap<>();
                userMap.put("userId", userId);
                allInters = commonMapper.selAllInterByUserId(userMap);
                userInfo.setAllInters(allInters);
                userInfo.setAllIntersFlag("1");
                if (redisTemplate.hasKey(token)) {
                    redisTemplate.delete(token);
                }
                redisTemplate.opsForValue().set(token, userInfo, 24L, TimeUnit.HOURS);
            } else {
                allInters = userInfo.getAllInters();
            }
            //用户名是否存在设置角色信息
            userDetailService.loadUserByUsername(token);
            //切换职位接口不能使用antMatchers忽略 有特殊参数
            if (requestUrl.contains("/login/getPositionRouter")) {
                return SecurityConfig.createList("PASS");
            }
            List<Map<String,Object>> list = new ArrayList<>();
            if (!"1".equals(userInfo.getAdminRoleFlag())) {
                UserInfo userInfo0 = (UserInfo)redisTemplate.opsForValue().get(token);
                Map<String,Object> map0 = new HashMap<>();
                map0.put("userId", userId);
                map0.put("positionId", userInfo0.getPositionId());
                map0.put("roleName", Constant.ROLE_ADMIN);
                list = commonMapper.queryAdminRole(map0);
                userInfo0.setAdminRoleList(list);
                userInfo0.setAdminRoleFlag("1");
                if (redisTemplate.hasKey(token)) {
                    redisTemplate.delete(token);
                }
                redisTemplate.opsForValue().set(token, userInfo0, 24L, TimeUnit.HOURS);
            } else {
                list = userInfo.getAdminRoleList();
            }
            if (list != null && list.size() > 0) {
                return SecurityConfig.createList("PASS");
            }
            //判断用户角色包含的菜单是否包含请求接口
            for (String interfacePath : Constant.initInterList) {
                if (antPathMatcher.match(interfacePath, requestUrl.split("\\?")[0])) {
                    return SecurityConfig.createList("PASS");
                }
            }
            if (requestUrl.indexOf("v1/sys/common/dict/code/") > -1) {
                return SecurityConfig.createList("PASS");
            }
            for (PriviInterCustom menu : menus) {
                if (!StringUtil.isEmpty(menu.getInterfaceId()) && antPathMatcher.match(menu.getInterfaceName(), requestUrl.split("\\?")[0])) {
                    return SecurityConfig.createList("PASS");
                }
            }
            String resName = "";
            for (PriviInterCustom inter : allInters) {
                if (!StringUtil.isEmpty(inter.getInterfaceId()) && antPathMatcher.match(inter.getInterfaceName(), requestUrl.split("\\?")[0])) {
                    resName = inter.getResName();
                    break;
                }
            }
            log.warn("接口无权 =========》interface=" + requestUrl + "@@interfaceName=" + resName);
            return SecurityConfig.createList("ILLEGAL_INTERFACE@" + resName);//不包含接口无权访问自定义角色
        } else {
            // token无效或redis中缓存已不存在
            return SecurityConfig.createList("SESSION_EXPIRED");
        }
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
    //是否包含管理员角色
    public boolean getFlag(boolean flag, List<PriviRoleCustom> roles) {
        for (int i = 0; i < roles.size(); i++) {
            if (Constant.ROLE_ADMIN.equals(roles.get(i).getRoleName())) {
                flag = true;
                break;
            } else {
                flag = false;
            }
        }
        return flag;
    }

    private boolean validToken(String token, HttpServletResponse response, HttpServletRequest request) {
        try {
            Claims claims = jwtTokenConfig.getClaimByToken(token);
            if (StringUtil.isEmpty(claims)) {
                log.warn("token不存在 =========》token=" + token);
                return false;
            }
            String userId = (String) claims.get(jwtTokenConfig.getSub());
            if (StringUtil.isEmpty(userId)) {
                log.warn("token不存在 =========》userId=" + userId + "@@token=" + token);
                return false;
            }
            boolean expireFlag = jwtTokenConfig.isTokenExpired(claims.getExpiration());
            if (expireFlag) {
                log.warn("token过期 =========》userId=" + userId + "@@token=" + token);
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
}
